<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta name="author" content="xmy6364">
    <meta name="description" content="想要成为大佬">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />

    <link rel="shortcut icon" href="/assets/favicon.ico" type="image/x-icon">
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@600&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/highlight.js/10.5.0/styles/default.min.css">
    <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/highlight.js/10.5.0/styles/github-gist.min.css">
    <link rel="stylesheet" href="/assets/lib/css/font-awesome.min.css">
    <link rel="stylesheet" href="/assets/css/layout.css" />
    <script defer src="https://cdn.bootcdn.net/ajax/libs/clipboard.js/2.0.6/clipboard.min.js"></script>
    <script defer src="/assets/js/copyCode.js"></script>
    <script defer src="/assets/js/backTop.js"></script>
    <script defer src="/assets/js/tool.js"></script>

    
  <link rel="stylesheet" href="/assets/css/page.css" />
  <link rel="stylesheet" href="/assets/css/sidebar.css" />
  <link rel="stylesheet" href="/assets/css/footer.css" />
  <link rel="stylesheet" href="/assets/css/post.css" />
  <script defer src="/assets/js/backHome.js"></script>
  <script defer src="/assets/js/toc.js"></script>
  <script defer src="/assets/js/copyright.js"></script>


    <title>JavaScript闭包</title>
  </head>
  <body>
    <div class="container">
      <aside>
        
  <div class="sidebar">
  <header>梦的博客</header>
  <div class="info">
    <div class="avatar">
      <img src="https://cdn.jsdelivr.net/gh/xmy6364/blog-image/img/pixelartoc_1.png" alt="头像">
    </div>
    <div class="author">xmy6364</div>
    <div class="description">想要成为大佬</div>
    <div class="about">
      <a href="/about">about me.</a>
    </div>
  </div>
  <div class="links">
    <ul>
    
      <li class="links-item">
        <a href="https://github.com/xmy6364" target="_blank">
          <i class="fa fa-github-alt" aria-hidden="true"></i>
        </a>
      </li>
    
      <li class="links-item">
        <a href="tencent://message/?uin=1176281967" target="_blank">
          <i class="fa fa-qq" aria-hidden="true"></i>
        </a>
      </li>
    
    </ul>
  </div>
  <nav>
    <ul>
    
      <li class="nav-item">
        <a href="/archives">
          <span class="nav-item__count">33</span>
          <span class="nav-item__label">归档</span>
        </a>
      </li>
    
      <li class="nav-item">
        <a href="/categories">
          <span class="nav-item__count">2</span>
          <span class="nav-item__label">分类</span>
        </a>
      </li>
    
      <li class="nav-item">
        <a href="/tags">
          <span class="nav-item__count">45</span>
          <span class="nav-item__label">标签</span>
        </a>
      </li>
    
    </ul>
  </nav>
  <div class="catalogue" id="catalogue"></div>
</div>

      </aside>
      <main>
        
  <div class="post">
    <div class="post-front">
      <h1 class="post-front__title">JavaScript闭包</h1>
      <div class="post-front__desc">
        
        <p class="post-front__desc-date">
          <i class="fa fa-calendar" aria-hidden="true"></i>
          2019/10/18 22:00:04
        </p>
        
        
        <p class="post-front__desc-category">
          <i class="fa fa-folder-o" aria-hidden="true"></i>
          <a href="/categories/技术">
            技术
          </a>
        </p>
        
          <div class="post-front__desc-tags">
          
          <a href="/tags/JavaScript">
            <i class="fa fa-tag" aria-hidden="true"></i>
            JavaScript
          </a>
          
        </div>
      </div>
    </div>
    <div class="post-content">
      <nav id="toc" class="toc"><ol><li><a href="#变量作用域">变量作用域</a></li><li><a href="#如何从外部读取函数内部声明的变量">如何从外部读取函数内部声明的变量？</a></li><li><a href="#闭包的特性">闭包的特性</a></li><li><a href="#使用闭包的注意点">使用闭包的注意点</a></li><li><a href="#参考文章">参考文章</a></li></ol></nav><p>闭包是JavaScript中的一个难点，同时也是它的特色，JavaScript的很多高级应用都要依靠闭包来实现。以下是我学习闭包的记录，希望对你有些帮助。</p>
<h2 id="变量作用域">变量作用域</h2>
<p>在学习闭包之前，我们首先要理解JavaScript不同与其他语言独特的变量作用域。在JavaScript中，不存在局部作用域的概念，但是有全局作用域以及函数作用域。全局作用域与其他语言的相同，没有需要注意的地方，而函数作用域是指函数内部声明的变量在函数外部无法直接访问。</p>
<pre class="hljs"><code><button class="copy-btn" type="button" data-clipboard-action="copy" data-clipboard-target="#copy1622198310330">复制</button><span class="hljs-keyword">var</span> a = <span class="hljs-number">99</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">f1</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(a);
}
f1();
<b class="name">javascript</b></code><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></pre><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy1622198310330">var a = 99;
function f1() {
    console.log(a);
}
f1();
</textarea>
<p>上面的代码中，f1可以读取到全局变量a，而下面的代码中a无法被访问。</p>
<pre class="hljs"><code><button class="copy-btn" type="button" data-clipboard-action="copy" data-clipboard-target="#copy1622198748158">复制</button><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">f1</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">var</span> a = <span class="hljs-number">99</span>;
}
<span class="hljs-built_in">console</span>.log(a);
<b class="name">javascript</b></code><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></pre><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy1622198748158">function f1() {
    var a = 99;
}
console.log(a);
</textarea>
<h2 id="如何从外部读取函数内部声明的变量">如何从外部读取函数内部声明的变量？</h2>
<p>在某些情况下，我们可能需要得到函数内部的变量，正常情况下是无法做到的，因此需要用特殊的办法。</p>
<pre class="hljs"><code><button class="copy-btn" type="button" data-clipboard-action="copy" data-clipboard-target="#copy1622202690777">复制</button><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">f1</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">var</span> a = <span class="hljs-number">99</span>;
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">f2</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.log(a);
    }
}
<b class="name">javascript</b></code><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></pre><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy1622202690777">function f1() {
    var a = 99;
    function f2() {
        console.log(a);
    }
}
</textarea>
<p>上面代码中，我们在函数f1中定义另一个函数f2，这样f1中的所有变量对于f2来说就是可见的，既然f2可以读取到f1中的变量，那么我们只要把f2作为f1的返回值，我们就可以在f1的外部读取到它内部的变量了。</p>
<pre class="hljs"><code><button class="copy-btn" type="button" data-clipboard-action="copy" data-clipboard-target="#copy1622200776786">复制</button><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">f1</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">var</span> a = <span class="hljs-number">99</span>;
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">f2</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.log(a);
    }
    <span class="hljs-keyword">return</span> f2;
}
<span class="hljs-keyword">var</span> result = f1();
result();
<b class="name">javascript</b></code><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy1622200776786">function f1() {
    var a = 99;
    function f2() {
        console.log(a);
    }
    return f2;
}
var result = f1();
result();
</textarea>
<p>此时，就形成了一个简单的闭包。因此，闭包就可以简单的理解为函数中的函数，而本质上，闭包就是一个连接函数内部和外部的桥梁。</p>
<h2 id="闭包的特性">闭包的特性</h2>
<p>闭包会使得函数中的变量都被保存到内存中。首先我们先看一下以下两个例子</p>
<pre class="hljs"><code><button class="copy-btn" type="button" data-clipboard-action="copy" data-clipboard-target="#copy1622196874552">复制</button><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">A</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">var</span> count = <span class="hljs-number">0</span>;
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">B</span>(<span class="hljs-params"></span>) </span>{
       count++;
       <span class="hljs-built_in">console</span>.log(count);
    }
    <span class="hljs-keyword">return</span> B;
}
<span class="hljs-keyword">var</span> C = A();
C(); <span class="hljs-comment">// 1</span>
C(); <span class="hljs-comment">// 2</span>
C(); <span class="hljs-comment">// 3</span>
<b class="name">javascript</b></code><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy1622196874552">function A() {
    var count = 0;
    function B() {
       count++;
       console.log(count);
    }
    return B;
}
var C = A();
C(); // 1
C(); // 2
C(); // 3
</textarea>
<p>count是函数A中的一个变量，它的值在函数B中被改变，函数B每执行一次，count的值就在原来的基础上累加1，因此，函数A中的count变量会一直保存在内存中。</p>
<pre class="hljs"><code><button class="copy-btn" type="button" data-clipboard-action="copy" data-clipboard-target="#copy1622198690886">复制</button><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">A</span>(<span class="hljs-params">x</span>) </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">B</span>(<span class="hljs-params">y</span>) </span>{
        <span class="hljs-built_in">console</span>.log(x+y);
    }
<span class="hljs-keyword">return</span> B;
}
<span class="hljs-keyword">var</span> C = A(<span class="hljs-number">3</span>);
C(<span class="hljs-number">5</span>); <span class="hljs-comment">//8</span>
<b class="name">javascript</b></code><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy1622198690886">function A(x) {
    function B(y) {
        console.log(x+y);
    }
return B;
}
var C = A(3);
C(5); //8
</textarea>
<p>当3传入A函数后，B函数就会记住这个值，所以在后面传入5的时候只会对B函数中的y赋值，所以最后会输出8。</p>
<h2 id="使用闭包的注意点">使用闭包的注意点</h2>
<p>由于上述闭包的特性，每次使用闭包都会大量增加内存的消耗，所以不能滥用闭包，否则会影响网页的性能。我们也可以在函数退出前，使函数内变量指向null来手动删除变量。我们可以来看下一道经典的面试题来理解。</p>
<pre class="hljs"><code><button class="copy-btn" type="button" data-clipboard-action="copy" data-clipboard-target="#copy1622198284109">复制</button><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">outer</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">var</span> num = <span class="hljs-number">0</span>; <span class="hljs-comment">//内部变量</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-comment">//通过return返回add函数，就可以在outer函数外访问了</span>
        num++; <span class="hljs-comment">//内部函数有引用，作为add函数的一部分了</span>
        <span class="hljs-built_in">console</span>.log(num);
    };
}
<span class="hljs-keyword">var</span> func1 = outer();
func1(); <span class="hljs-comment">//实际上是调用add函数， 输出1</span>
func1(); <span class="hljs-comment">//输出2 因为outer函数内部的私有作用域会一直被占用</span>
<span class="hljs-keyword">var</span> func2 = outer();
func2(); <span class="hljs-comment">// 输出1  每次重新引用函数的时候，闭包是全新的。</span>
func2(); <span class="hljs-comment">// 输出2  </span>
<b class="name">javascript</b></code><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy1622198284109">function outer(){
    var num = 0; //内部变量
    return function add() { //通过return返回add函数，就可以在outer函数外访问了
        num++; //内部函数有引用，作为add函数的一部分了
        console.log(num);
    };
}
var func1 = outer();
func1(); //实际上是调用add函数， 输出1
func1(); //输出2 因为outer函数内部的私有作用域会一直被占用
var func2 = outer();
func2(); // 输出1  每次重新引用函数的时候，闭包是全新的。
func2(); // 输出2  
</textarea>
<h2 id="参考文章">参考文章</h2>
<ul>
<li><a href="https://github.com/ljianshu/Blog/issues/6">深入浅出Javascript闭包</a></li>
<li><a href="https://www.cnblogs.com/onepixel/p/5062456.html">让你分分钟理解JavaScript闭包</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html">学习Javascript闭包（Closure）</a></li>
</ul>

    </div>
    
  </div>

        <footer>
        
  <div class="footer">
  
  <div class="theme">
    博客主题为 <a href="https://github.com/xmy6364/CoinRailgun">CoinRailgun</a> 默认主题
  </div>
  <div class="copyright">
    <span>© 2019-2021 xmy6364.</span>
    <span>Powered by <a href="https://github.com/xmy6364/CoinRailgun">CoinRailgun</a></span>
  </div>
</div>

        </footer>
      </main>
    </div>
  </body>
</html>
